package org.yzh.commons.util;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.text.SimpleDateFormat;

@Slf4j
public class JackJsonUtils {
    private final static ObjectMapper objectMapper = new ObjectMapper();
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    static {
        /**
         * 格式化日期格式
         */
        objectMapper.setDateFormat(sdf);
        /**
         * 该特性决定parser将是否允许解析使用Java/C++ 样式的注释（包括'/'+'*' 和'//' 变量）。 由于JSON标准说明书上面没有提到注释是否是合法的组成，所以这是一个非标准的特性；
         * 尽管如此，这个特性还是被广泛地使用。
         *
         * 注意：该属性默认是false，因此必须显式允许，即通过JsonParser.Feature.ALLOW_COMMENTS 配置为true。
         *
         */
        objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
        /**
         * 这个特性决定parser是否将允许使用非双引号属性名字， （这种形式在Javascript中被允许，但是JSON标准说明书中没有）。
         *
         * 注意：由于JSON标准上需要为属性名称使用双引号，所以这也是一个非标准特性，默认是false的。
         * 同样，需要设置JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES为true，打开该特性。
         *
         */
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        /**
         * 该特性决定parser是否允许单引号来包住属性名称和字符串值。
         *
         * 注意：默认下，该属性也是关闭的。需要设置JsonParser.Feature.ALLOW_SINGLE_QUOTES为true
         *
         */
        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        /**
         * 该特性决定parser是否允许JSON字符串包含非引号控制字符（值小于32的ASCII字符，包含制表符和换行符）。 如果该属性关闭，则如果遇到这些字符，则会抛出异常。
         * JSON标准说明书要求所有控制符必须使用引号，因此这是一个非标准的特性。
         *
         * 注意：默认时候，该属性关闭的。需要设置：JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS为true。
         *
         */
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
        /**
         * 该特性决定JSON对象属性名称是否可以被String#intern 规范化表示。
         *
         * 如果允许，则JSON所有的属性名将会 intern() ；如果不设置，则不会规范化，
         *
         * 默认下，该属性是开放的。此外，必须设置CANONICALIZE_FIELD_NAMES为true
         *
         * 关于intern方法作用：当调用 intern 方法时，如果池已经包含一个等于此 String 对象的字符串 （该对象由 equals(Object) 方法确定），则返回池中的字符串。否则，将此 String
         * 对象添加到池中， 并且返回此 String 对象的引用。
         *
         * @since 1.3
         */
//        objectMapper.configure(JsonParser.Feature.INTERN_FIELD_NAMES, true);
        /**
         * 该特性决定JSON对象的属性名称是否被规范化。
         *
         * @since 1.5
         */
//        objectMapper.configure(JsonParser.Feature.CANONICALIZE_FIELD_NAMES, true);
        /**
         * 该特性决定了当遇到未知属性（没有映射到属性，没有任何setter或者任何可以处理它的handler），是否应该抛出一个
         * JsonMappingException异常。这个特性一般式所有其他处理方法对未知属性处理都无效后才被尝试，属性保留未处理状态。
         *
         * 默认情况下，该设置是被打开的。
         *
         * @since 1.2
         */
//        objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        /**
         * 该特性决定了当遇到未知属性（没有映射到属性，没有任何setter或者任何可以处理它的handler），是否应该抛出一个
         * JsonMappingException异常。这个特性一般式所有其他处理方法对未知属性处理都无效后才被尝试，属性保留未处理状态。
         *
         * 默认情况下，该设置是被打开的。
         *
         * @since 1.2
         */
//        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    private JackJsonUtils() {
    }

    /**
     * 将json 对象序列化成string
     * objectToJsonString(对象、集合)
     *
     * @param obj
     * @return
     */
    public static String objectToJsonString(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (JsonGenerationException e) {
            log.error("encode(Object)", e); //$NON-NLS-1$
        } catch (JsonMappingException e) {
            log.error("encode(Object)", e); //$NON-NLS-1$
        } catch (IOException e) {
            log.error("encode(Object)", e); //$NON-NLS-1$
        }
        return null;
    }

    /**
     * 将json string反序列化成对象
     * Customer customer=JackJsonUtils.jsonStringToObject(newstr,Customer.class);
     *
     * @param json
     * @param valueType
     * @return
     */
    public static <T> T jsonStringToObject(String json, Class<T> valueType) {
        try {
            return objectMapper.readValue(json, valueType);
        } catch (JsonParseException e) {
            log.error("decode(String, Class<T>)", e);
        } catch (JsonMappingException e) {
            log.error("decode(String, Class<T>)", e);
        } catch (IOException e) {
            log.error("decode(String, Class<T>)", e);
        }
        return null;
    }

    /**
     * 将json array反序列化为对象
     * List<Customer> customers1=JackJsonUtils.jsonStringToListObject(newstr,new TypeReference<List<Customer>>(){});
     *
     * @param json
     * @param typeReference
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T jsonStringToListObject(String json, TypeReference<T> typeReference) {
        try {
            return (T) objectMapper.readValue(json, typeReference);
        } catch (JsonParseException e) {
            log.error("decode(String, JsonTypeReference<T>)", e);
        } catch (JsonMappingException e) {
            log.error("decode(String, JsonTypeReference<T>)", e);
        } catch (IOException e) {
            log.error("decode(String, JsonTypeReference<T>)", e);
        }
        return null;
    }

    /**
     * 将json array反序列化为对象
     * List<Customer> customers1=JackJsonUtils.jsonStringToListObject(newstr,new TypeReference<List<Customer>>(){});
     *
     * @param json
     * @param typeReference
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T jsonStringToListObjectSecond(String json, TypeReference<T> typeReference) throws JsonProcessingException {

        return (T) objectMapper.readValue(json, typeReference);

    }
}